package com.xckj.cycling.viewmodel

import android.graphics.Bitmap
import android.location.Location
import androidx.core.os.CancellationSignal
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import com.xckj.cycling.framework.lazyGlobalViewModel
import com.junca.android.service.Request
import com.mapbox.geojson.LineString
import com.mapbox.geojson.Point
import com.mapbox.navigation.base.route.NavigationRoute
import com.mapbox.navigation.base.route.toDirectionsRoutes
import com.mapbox.navigation.utils.internal.toPoint
import com.xckj.cycling.context.AppContext
import com.xckj.cycling.framework.model.Response
import com.xckj.cycling.model.*
import com.xckj.cycling.repository.FileRepository
import com.xckj.cycling.repository.MapRepository
import kotlinx.coroutines.*
import org.json.JSONObject
import java.io.File
import java.io.FileNotFoundException
import java.io.FileOutputStream
import java.io.IOException
import java.text.SimpleDateFormat
import java.util.*
import kotlin.collections.ArrayList


class MapViewModel : ViewModel() {

    companion object {
        const val TAG = "MapViewModel"
    }

    /**
     * 精度
     */
    var accuracy: Double = 0.0
    val routes: MutableLiveData<List<NavigationRoute>> = MutableLiveData()
    val endName: MutableLiveData<String> = MutableLiveData()
    private val userViewModel: LoginViewModel by lazyGlobalViewModel()

    //    val searchEngine = SearchEngine.createSearchEngineWithBuiltInDataProviders(
//        apiType = ApiType.GEOCODING,
//        settings = SearchEngineSettings(
//            AppContext.globalContext().getString(R.string.mapbox_access_token)
//        )
//    )
    private val repository = MapRepository()

    private var lastAltitude = Double.MIN_VALUE
    private var startAltitude = -1.0
    private var lastLocation: Location? = null
    var lastRecordLocation: Location? = null
    private var lastPoint: Point? = null
    var lastRecordPoint: Point? = null

    var lastRecordPointLiveData = MutableLiveData<Point?>()
    private var endPoint: Point? = null //终点
    private var lastCancel: CancellationSignal? = null

    val routeLiveData = MutableLiveData<NavigationToStartModel>()

    val pointsLiveData = MutableLiveData<MutableList<RoutePoint>>()

    val lineStringLiveData = MutableLiveData<Route>()

    //高度的liveData
    val altitudeLiveData: MutableLiveData<Double> = MutableLiveData(0.0)
    fun setCurrentRoute(routes: List<NavigationRoute>, lineStart: Point, end: Point) {
        val l = navigationToPoints(routes)
        lineStringLiveData.value = Route(l, lineStart, end)
    }

    private var stop = true

    fun locationChanged(location: Location) {
        if (stop) {
            return
        }
        if (lastLocation == null) {
            startRoute(location)
        }
        lastLocation = location
        if (lastAltitude == Double.MIN_VALUE) {
            startAltitude = location.altitude
            lastAltitude = location.altitude
            return
        }
        val now = location.altitude
        val diff = now - lastAltitude
        if (Math.abs(diff) <= 0.1) {
            lastAltitude = location.altitude
            return
        }
        //计算高度差
        if (diff > 0) {
            altitudeLiveData.value = altitudeLiveData.value?.plus(diff)
        }
        lastAltitude = location.altitude
    }

    private var lastCallback: (Response<Boolean>) -> Unit = {}
    fun start(callback: (Response<Boolean>) -> Unit) {
        stop = false
        lastLocation = null
        lastAltitude = Double.MIN_VALUE
        cid = null
        lastCallback = callback
        lastRecordPoint?.let {
            lastPoint = it
            startRoute(it)
        }
    }

    fun stop(bitmap: Bitmap, callback: (Response<Any>) -> Unit) {
        stop = true
        lastCancel?.cancel()
        stopRout(bitmap, callback)
        lastLocation = null
    }

    fun pause() {
        setState(2)
    }

    fun resume() {
        setState(3)
    }


    private fun setState(state: Int) {
        //骑行状态:1=继续骑行,2=暂停骑行,3=结束骑行
        viewModelScope.launch {
            val r =
                Request.postSuspend<Any>("qixing/cycling/suspendend", HashMap<String, Any>().apply {
                    put("cid", cid ?: "")
                    put("state", state)
                })
        }
    }

    var cid: String? = null
    private fun stopRout(bitmap: Bitmap, callback: (Response<Any>) -> Unit) {
        viewModelScope.launch {
            val file = withContext(Dispatchers.IO) {
                saveBitmap(bitmap)
            }
            val uploadResultModel = FileRepository().upload(file)
            val r =
                Request.postSuspend<Any>("qixing/cycling/suspendend", HashMap<String, Any>().apply {
                    put("cid", cid ?: "")
                    put("state", 3)
                    put("file", uploadResultModel.data?.url ?: "")
                    lastRecordPoint?.let {
                        put("end_lng", it.longitude().toString())
                        put("end_lat", it.latitude().toString())
                    }
                })
            pointsLiveData.value = ArrayList()
            lineStringLiveData.value = Route()
            callback.invoke(r)
        }
    }

    private fun startRoute(location: Location) {
        viewModelScope.launch {
            val r =
                Request.postSuspend<StartRideModel>(
                    "qixing/cycling/start",
                    HashMap<String, Any>().apply {
                        put(
                            "equipmentid",
                            userViewModel.userCenterLiveData.value?.equipment?.id.toString()
                        )
//                        put("user_post", getName(start))
                        put("user_alt", location.altitude.toString())
                        put("user_lat", location.latitude.toString())
                        put("user_lng", location.longitude.toString())
//                        put("distance", d.distance)
//                        put("end_post", getName(end).apply {
//                            endName.value = this
//                        })
//                        put("end_lng", endPoint?.longitude().toString())
//                        put("end_lat", endPoint?.latitude().toString())
//                        put("estimatedDuration", d.duration)
                    })
            lastCallback.invoke(Response.success(true))
            lastCallback = {}
            if (r.isSuccessful) {
                startHeartBeat(r.data?.cid ?: "")
            }
        }
    }

    private fun startRoute(point: Point) {
        viewModelScope.launch {
            val r =
                Request.postSuspend<StartRideModel>(
                    "qixing/cycling/start",
                    HashMap<String, Any>().apply {
                        put(
                            "equipmentid",
                            userViewModel.userCenterLiveData.value?.equipment?.id.toString()
                        )
//                        put("user_post", "123")
                        put("user_alt", point.altitude().toString())
                        put("user_lat", point.latitude().toString())
                        put("user_lng", point.longitude().toString())
//                        put("distance", d.distance)
//                        put("end_post", "123")
//                        put("end_lng", endPoint?.longitude().toString())
//                        put("end_lat", endPoint?.latitude().toString())
//                        put("estimatedDuration", d.duration)
                    })
            lastCallback.invoke(Response.success(true))
            lastCallback = {}
            if (r.isSuccessful) {
                startHeartBeat(r.data?.cid ?: "")
            }
        }
    }


    private fun getName(featureCollection: MapBoxAddressModel): String {
        if (featureCollection.features.isNullOrEmpty()) {
            return "Unknown"
        }
        return featureCollection.features?.get(0)?.place_name
            ?: "Unknown"
    }

    val heartBeatLiveData = MutableLiveData<HeartBeatModel?>()
    private fun startHeartBeat(cid: String) {
        this.cid = cid
        lastCancel?.cancel()
        val cancellationSignal = CancellationSignal()
        lastCancel = cancellationSignal
        viewModelScope.launch {
            while (isActive && !cancellationSignal.isCanceled) {
                val r = Request.postSuspend<HeartBeatModel>(
                    "qixing/cycling/ridingHeartbeat",
                    HashMap<String, Any>().apply {
                        put("cid", cid)
                        put("heartbeat", obtainJson())
                    })
                heartBeatLiveData.value = r.data
                delay(3000)
            }
        }
    }

    fun addRouteToPrivate(
        data: RouteLineModel?,
        model: AddRouteRequestModel,
        callback: (Response<List<String>>) -> Unit
    ) {
        viewModelScope.launch {
            val imgResult = arrayListOf<String>()
            val string = if (model.imgs.isEmpty()) {
                ""
            } else {
                val imgs = model.imgs.map {
                    if (it.startsWith("http")) {
                        it.replaceFirst("http", "").also {
                            imgResult.add(it)
                        }
                    } else {
                        Request.postWithFile<UploadResultModel>(
                            "common/upload",
                            HashMap<String, File>().apply {
                                put("file", File(it))
                            }).data?.let {
                            imgResult.add(it.fullurl ?: "")
                            it.url ?: ""
                        }
                    }
                }
                var str = StringBuilder()
                imgs.forEach {
                    str = str.append(it)
                    str = str.append(",")
                }
                str.substring(0, str.length - 1)
            }
            val str =
                if (data == null) "qixing/cycling/addPrivate" else "qixing/cycling/editorPrivate"
            val r = Request.postSuspend<Any>(
                str,
                HashMap<String, Any>().apply {
                    put("id", (data?.id ?: cid) ?: "")
                    put("name", model.name)
                    put("bicycleType", model.bicycleType)
                    put("ground", model.ground)
                    put("share", model.share)
                    put("linestatus", model.linestatus)
                    put("imgs", string)
                    put("introduce", model.introduce)

                })
            callback.invoke(r.let {
                if (it.isSuccessful) {
                    Response.success(imgResult)
                } else {
                    Response.error(r.code, msg = r.msg)
                }
            })
        }
    }

    val navigationDetail: MutableLiveData<NavigationModel> = MutableLiveData()
    fun loadNavigationDetail(id: String = cid ?: "") {
        viewModelScope.launch {
            navigationDetail.value = null
            val r = Request.postSuspend<NavigationModel>(
                "qixing/cycling/cyclingDetail",
                HashMap<String, Any>().apply {
                    put("id", id)
                })
            navigationDetail.value = r.data
        }
    }

    fun loadNavigationDetailCallBack(id: String, callback: (Response<NavigationModel>) -> Unit) {
        viewModelScope.launch {
            val r = Request.postSuspend<NavigationModel>(
                "qixing/cycling/cyclingDetail",
                HashMap<String, Any>().apply {
                    put("id", id)
                })
            callback.invoke(r)
        }
    }


    private fun obtainJson(): JSONObject {
        return JSONObject().apply {
            lastRecordPoint?.let {
                put("alt", it.altitude().toString())
                put("lat", it.latitude().toString())
                put("lng", it.longitude().toString())
                put("accuracy", accuracy.toString())
                put("heartrate", "0") //心率 默认0
            }
        }
    }

    fun locationChanged(location: Point) {
        if (location.latitude() != lastRecordPoint?.latitude() || location.longitude() != lastRecordPoint?.longitude()) {
            lastRecordPointLiveData.value = location
        }
        lastRecordPoint = location
        if (stop) {
            return
        }
        if (lastPoint == null) {
            startRoute(location)
        }
        lastPoint = location
        if (lastAltitude == Double.MIN_VALUE) {
            startAltitude = location.altitude()
            lastAltitude = location.altitude()
            return
        }
        val now = location.altitude()
        val diff = now - lastAltitude
        if (Math.abs(diff) <= 0.1) {
            lastAltitude = location.altitude()
            return
        }
        //计算高度差
        if (diff > 0) {
            altitudeLiveData.value = altitudeLiveData.value?.plus(diff)
        }
        lastAltitude = location.altitude()
    }


    fun saveBitmap(bitmap: Bitmap): String {
        val dir: String = AppContext.globalContext().filesDir.absolutePath + "/screen_shot/"
        val file = File(dir)
        if (!file.exists()) {
            file.mkdirs()
        }
        val currentTime = System.currentTimeMillis()
        val sdf = SimpleDateFormat("yyyyMMdd_HHmmssSSS")
        val time: String = sdf.format(Date(currentTime))
        val f = File(dir, "$time.jpg")
        if (f.exists()) {
            f.delete()
        }
        try {
            val out = FileOutputStream(f)
            bitmap.compress(Bitmap.CompressFormat.PNG, 100, out)
            out.flush()
            out.close()
        } catch (e: FileNotFoundException) {
            e.printStackTrace()
        } catch (e: IOException) {
            e.printStackTrace()
        }
        return f.absolutePath
    }

    fun setPoints(json: List<RoutePoint>?) {
        pointsLiveData.value = ArrayList(json ?: emptyList())
    }

    private fun navigationToPoints(list: List<NavigationRoute>): LineString {
        val route = list.toDirectionsRoutes().get(0)
        return LineString.fromPolyline(
            route.geometry() ?: "",
            com.mapbox.core.constants.Constants.PRECISION_6
        )
    }

    fun refreshLocation(callback: (Response<Point>) -> Unit = {}) {
        viewModelScope.launch {
            repository.location()?.toPoint()?.let {
                locationChanged(it)
                callback.invoke(Response.success(it))
            }
        }

    }

}